package thread.wang_bao_ling.c22.executors;

import org.junit.Test;

import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * ScheduledExecutorService主要是用来解决Timer的不足而设计的
 * 还是使用TimerTest中的那三个task（task1抛出了一个异常）做实验，可以发现没有了Timer的那两个缺点（1.任务异常不会导致整个任务调度直接结束了 2. 某个任务执行时间较长，不会影响下个任务执行）
 * ScheduledExecutorService实现原理就是线程池+自己实现的一个延时队列{@link java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue}，
 * 这个延时队列中的元素就是ScheduledFutureTask类的实例对象，ScheduledFutureTask类继承自FutureTask且实现了RunnableScheduledFuture（该接口实现了Delayed接口）接口
 * 所以通过schedule等方法传入定时任务的参数后实例化成ScheduledFutureTask任务对象既能添加到优先队列中，又能添加到线程池中供线程池消费
 * 相关链接{@link thread.wang_bao_ling.c20.DelayQueueTest} {@link thread.wang_bao_ling.c20.PriorityBlockingQueueTest}
 */
public class ScheduledThreadPoolExecutorTest {

    @Test
    public void testInfluence(){
        ScheduledExecutorService  threadPoolExecutor= Executors.newScheduledThreadPool(10);
        //task1 执行要两秒
        TimerTask task1 = new TimerTask() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task1 begin---->" + Thread.currentThread().getName() + "---->currentTime:" + System.currentTimeMillis() / 1000);
                //任务1异常后不会影响任务二
                throw new RuntimeException();
            }
        };

        TimerTask task3 = new TimerTask() {
            @Override
            public void run() {
                System.out.println("task2 begin---->"+Thread.currentThread().getName()+"---->currentTime:" + System.currentTimeMillis() / 1000);
            }
        };
        threadPoolExecutor.schedule(task1,0,TimeUnit.SECONDS);
        threadPoolExecutor.scheduleAtFixedRate(task3,0,1,TimeUnit.SECONDS);
    }

    /**
     * 测试固定延时打印
     * 任务执行时间为2s，任务间隔时间为1s，最终打印的间隔时间为3s，因为ScheduledExecutorService中固定延时任务的下次执行时间计算是在当前任务执行后，计算逻辑在下面的setNextRunTime方法（p > 0 逻辑）
     * {@link ScheduledThreadPoolExecutor.ScheduledFutureTask#setNextRunTime()}
     * 比如说当前任务是在11:11 30s执行，任务执行时间为2s，延时时间为1s，依照计算公式now()+ delay=(11:11 30s + 2s) + 1s = 11:11 33s 所以距离上一次打印的间隔为3s
     * @throws InterruptedException
     */
    @Test
    public void testScheduleWithFixedDelay() throws InterruptedException {
        ScheduledExecutorService  scheduledThreadPool= Executors.newScheduledThreadPool(10);
        scheduledThreadPool.scheduleWithFixedDelay(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"----currentTime:" + System.currentTimeMillis() / 1000);
        }, 0, 1, TimeUnit.SECONDS);

        TimeUnit.SECONDS.sleep(20); //防止单元测试线程直接结束
    }

    /**
     * 测试固定速率延时打印
     * 任务执行时间为2s，任务间隔时间为1s，最终打印的间隔时间为2s，因为ScheduledExecutorService中固定速率任务的下次执行时间计算是在当前任务执行后，计算逻辑在下面的setNextRunTime方法（p < 0）
     * {@link ScheduledThreadPoolExecutor.ScheduledFutureTask#setNextRunTime()}
     * 比如说当前任务是在11:11 30s执行，任务执行时间为2s，延时时间为1s，依照计算公式time + delay=(11:11 30s + 1s) + 1s = 11:11 31s 所以打印间隔时间是2s（虽然下次执行时间早到了，但当前任务还没执行完）
     * @throws InterruptedException
     */
    @Test
    public void testScheduleAtFixedRate() throws InterruptedException {
        ScheduledExecutorService  scheduledThreadPool= Executors.newScheduledThreadPool(10);
        scheduledThreadPool.scheduleAtFixedRate(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"----currentTime:" + System.currentTimeMillis() / 1000);
        }, 0, 1, TimeUnit.SECONDS);

        TimeUnit.SECONDS.sleep(200); //防止单元测试线程直接结束
    }
}
